package com.jkj.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

public class FileUtils {
    public static final char SEPARATOR_CHAR = '/';
    public final static String FILE_EXTENSION_SEPARATOR = ".";
    private static final String FOLDER_SEPARATOR = "/";
    private static final char EXTENSION_SEPARATOR = '.';

    private FileUtils() {
        throw new AssertionError();
    }

    /**
     * 非递归遍历目录
     *
     * @param dir 文件目录
     */
    public static List<File> scanDir(File dir) {
        List<File> list = new ArrayList<File>();
        File file[] = dir.listFiles();
        List<File> dirList = new ArrayList<File>();
        //获得所有的目录
        for (int i = 0; i < file.length; i++) {
            if (file[i].isDirectory())
                dirList.add(file[i]);
            else {
                list.add(file[i]);
            }
        }
        //不停的扫描目录
        while (true) {
            if (dirList.size() == 0) {
                break;
            }
            File files[] = dirList.get(0).listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    dirList.add(f);
                } else {
                    list.add(f);
                }
            }
            dirList.remove(0);
        }
        return list;
    }

    /**
     * 非递归遍历目录
     *
     * @param dir
     * @param exts
     * @return
     */
    public static List<File> scanDir(File dir, String... exts) {
        List<File> list = new ArrayList<File>();
        File file[] = dir.listFiles();
        List<File> dirList = new ArrayList<File>();
        // 获得所有的目录
        for (int i = 0; i < file.length; i++) {
            if (file[i].isDirectory())
                dirList.add(file[i]);
            else {
                for (String ext : exts) {
                    if (getFileExtension(file[i].getAbsolutePath()).toLowerCase().equals(ext.toLowerCase()))
                        list.add(file[i]);
                }
            }
        }
        // 不停的扫描目录
        while (true) {
            if (dirList.size() == 0) {
                break;
            }
            File files[] = dirList.get(0).listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    dirList.add(f);
                } else {
                    for (String ext : exts) {
                        if (getFileExtension(f.getAbsolutePath()).toLowerCase().equals(ext.toLowerCase()))
                            list.add(f);
                    }
                }
            }
            dirList.remove(0);
        }
        return list;
    }

    /**
     * 功能：复制文件或者文件夹。
     *
     * @param inputFile   源文件
     * @param outputFile  目的文件
     * @param isOverWrite 是否覆盖(只针对文件)
     * @throws IOException
     */
    public static void copy(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
        if (!inputFile.exists()) {
            throw new RuntimeException(inputFile.getPath() + "源目录不存在!");
        }
        copyPri(inputFile, outputFile, isOverWrite);
    }

    /**
     * 功能：为copy 做递归使用。
     *
     * @param inputFile
     * @param outputFile
     * @param isOverWrite
     * @throws IOException
     */
    private static void copyPri(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
        // 是个文件。
        if (inputFile.isFile()) {
            copySimpleFile(inputFile, outputFile, isOverWrite);
        } else {
            // 文件夹
            if (!outputFile.exists()) {
                outputFile.mkdir();
            }
            // 循环子文件夹
            for (File child : inputFile.listFiles()) {
                copy(child, new File(outputFile.getPath() + "/" + child.getName()), isOverWrite);
            }
        }
    }

    public static void copy(File inputFile, String outputFilePath, boolean isOverWrite) throws IOException {
        File destFile = new File(outputFilePath + File.separator + inputFile.getName());
        copy(inputFile, destFile, isOverWrite);
    }


    /**
     * 功能：copy单个文件
     *
     * @param inputFile   源文件
     * @param outputFile  目标文件
     * @param isOverWrite 是否允许覆盖
     * @throws IOException
     */
    private static void copySimpleFile(File inputFile, File outputFile, boolean isOverWrite) throws IOException {
        // 目标文件已经存在
        if (outputFile.exists()) {
            if (isOverWrite) {
                if (!outputFile.delete()) {
                    throw new RuntimeException(outputFile.getPath() + "无法覆盖！");
                }
            } else {
                // 不允许覆盖
                return;
            }
        }
        InputStream in = new FileInputStream(inputFile);
        OutputStream out = new FileOutputStream(outputFile);
        byte[] buffer = new byte[1024];
        int read = 0;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        out.close();
    }

    /**
     * 功能：删除文件
     *
     * @param file 文件
     */
    public static void delete(File file) {
        deleteFile(file);
    }

    /**
     * 功能：删除文件，内部递归使用
     *
     * @param file 文件
     * @return boolean true 删除成功，false 删除失败。
     */
    private static void deleteFile(File file) {
        if (file == null || !file.exists()) {
            return;
        }
        // 单文件
        if (!file.isDirectory()) {
            boolean delFlag = file.delete();
            if (!delFlag) {
                throw new RuntimeException(file.getPath() + "删除失败！");
            } else {
                return;
            }
        }
        // 删除子目录
        for (File child : file.listFiles()) {
            deleteFile(child);
        }
        // 删除自己
        file.delete();
    }

    /**
     * 重命名文件
     *
     * @param filePath    文件目录
     * @param oldFileName 旧文件名
     * @param newFileName 新文件名
     */
    public static void rename(String filePath, String oldFileName, String newFileName) {
        File oldFile = new File(filePath + File.separator + oldFileName);
        File newFile = new File(filePath + File.separator + newFileName);
        moveFile(oldFile, newFile);
    }

    /**
     * 功能：保存文件。
     *
     * @param content 字节
     * @param file    保存到的文件
     * @throws IOException
     */
    public static void save(byte[] content, File file) throws IOException {
        if (file == null) {
            throw new RuntimeException("保存文件不能为空");
        }
        if (content == null) {
            throw new RuntimeException("文件流不能为空");
        }
        InputStream is = new ByteArrayInputStream(content);
        save(is, file);
    }

    /**
     * 功能：保存文件
     *
     * @param streamIn 文件流
     * @param file     保存到的文件
     * @throws IOException
     */
    public static void save(InputStream streamIn, File file) throws IOException {
        if (file == null) {
            throw new RuntimeException("保存文件不能为空");
        }
        if (streamIn == null) {
            throw new RuntimeException("文件流不能为空");
        }
        // 输出流
        OutputStream streamOut = null;
        // 文件夹不存在就创建。
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        streamOut = new FileOutputStream(file);
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = streamIn.read(buffer, 0, 8192)) != -1) {
            streamOut.write(buffer, 0, bytesRead);
        }
        streamOut.close();
        streamIn.close();
    }

    /**
     * 读取文件返回StringBuilder
     *
     * @param filePath
     * @param charsetName "utf-8"
     * @return
     */
    public static StringBuilder readFile(String filePath, String charsetName) {
        File file = new File(filePath);
        StringBuilder fileContent = new StringBuilder("");
        if (file == null || !file.isFile()) {
            return null;
        }
        BufferedReader reader = null;
        try {
            InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
            reader = new BufferedReader(is);
            String line = null;
            while ((line = reader.readLine()) != null) {
                if (!fileContent.toString().equals("")) {
                    fileContent.append("\r\n");
                }
                fileContent.append(line);
            }
            reader.close();
            return fileContent;
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException("IOException occurred. ", e);
                }
            }
        }
    }

    /**
     * 向某个文件中写入内容
     *
     * @param filePath 文件路径
     * @param content  写入的内容
     * @param append   是否追加写入
     * @return
     */
    public static boolean writeFile(String filePath, String content, boolean append) {
        if (StrUtil.isEmpty(content)) {
            return false;
        }

        FileWriter fileWriter = null;
        try {
            makeDirs(filePath);
            fileWriter = new FileWriter(filePath, append);
            fileWriter.write(content);
            fileWriter.close();
            return true;
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    throw new RuntimeException("IOException occurred. ", e);
                }
            }
        }
    }

    /**
     * 将文件写入到制定目录
     *
     * @param fileName
     * @param data
     * @param append
     * @return
     */
    public static boolean writeFileToSD(String fileName, String data, boolean append, String root) {
        boolean flag = false;
        FileWriter fileWriter = null;
        if (root != null) {
            String filePath = root + "/" + fileName;
            try {
                makeDirs(filePath);
                fileWriter = new FileWriter(filePath, append);
                fileWriter.write(data);
                fileWriter.write("\r\n");
                fileWriter.close();
                flag = true;
            } catch (IOException e) {
                throw new RuntimeException("IOException occurred. ", e);
            } finally {
                if (fileWriter != null) {
                    try {
                        fileWriter.close();
                    } catch (IOException e) {
                        throw new RuntimeException("IOException occurred. ", e);
                    }
                }
            }
        }
        return flag;
    }

    /**
     * 将文件Basa64得到字符串
     *
     * @param fileName
     * @return
     */
    public static String getFileBase64Encoding(String fileName) {
        return Base64.encode(getFileByte(fileName));
    }

    /**
     * 将Base64字符串保存为文件
     *
     * @param base64Str
     */
    public static void saveFileByBase64String(String base64Str, String filePath) {
        saveFileFromByte(Base64.decode(base64Str), filePath);
    }

    /**
     * 根据文件得到byte
     *
     * @param fileName
     * @return
     */
    public static byte[] getFileByte2(String fileName) {
        File file = new File(fileName);
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    /**
     * 向某个文件中写入内容
     *
     * @param filePath    文件路径
     * @param contentList 写入的内容集合 每写一个换行
     * @param append      是否追加写入
     * @return
     */
    public static boolean writeFile(String filePath, List<String> contentList, boolean append) {
        if (CollUtil.isEmpty(contentList)) {
            return false;
        }

        FileWriter fileWriter = null;
        try {
            makeDirs(filePath);
            fileWriter = new FileWriter(filePath, append);
            int i = 0;
            for (String line : contentList) {
                if (i++ > 0) {
                    fileWriter.write("\r\n");
                }
                fileWriter.write(line);
            }
            fileWriter.close();
            return true;
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    throw new RuntimeException("IOException occurred. ", e);
                }
            }
        }
    }

    /**
     * 向某个文件中写入内容 全新写入(不追加)
     *
     * @param filePath 文件路径
     * @param content  写入的内容
     * @return
     */
    public static boolean writeFile(String filePath, String content) {
        return writeFile(filePath, content, false);
    }

    /**
     * 向某个文件中写入内容 全新写入(不追加)
     *
     * @param filePath    文件路径
     * @param contentList 写入的内容集合 每写一个换行
     * @return
     */
    public static boolean writeFile(String filePath, List<String> contentList) {
        return writeFile(filePath, contentList, false);
    }

    /**
     * 向某个文件中写入内容 全新写入(不追加)
     *
     * @param filePath 文件路径
     * @param stream   输入流
     * @return
     */
    public static boolean writeFile(String filePath, InputStream stream) {
        return writeFile(filePath, stream, false);
    }

    /**
     * 向某个文件中写入内容
     *
     * @param filePath 文件路径
     * @param stream   输入流
     * @param append   是否追加写入
     * @return
     */
    public static boolean writeFile(String filePath, InputStream stream, boolean append) {
        return writeFile(filePath != null ? new File(filePath) : null, stream, append);
    }

    /**
     * 向某个文件中写入内容 不追加写入
     *
     * @param file   文件
     * @param stream 输入流
     * @return
     */
    public static boolean writeFile(File file, InputStream stream) {
        return writeFile(file, stream, false);
    }

    /**
     * 向某个文件中写入内容 不追加写入
     *
     * @param file   文件
     * @param stream 输入流
     * @param append 是否追加写入
     * @return
     */
    public static boolean writeFile(File file, InputStream stream, boolean append) {
        OutputStream o = null;
        try {
            makeDirs(file.getAbsolutePath());
            o = new FileOutputStream(file, append);
            byte data[] = new byte[1024];
            int length = -1;
            while ((length = stream.read(data)) != -1) {
                o.write(data, 0, length);
            }
            o.flush();
            return true;
        } catch (FileNotFoundException e) {
            throw new RuntimeException("FileNotFoundException occurred. ", e);
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (o != null) {
                try {
                    o.close();
                    stream.close();
                } catch (IOException e) {
                    throw new RuntimeException("IOException occurred. ", e);
                }
            }
        }
    }

    /**
     * 移动文件位置
     *
     * @param sourceFilePath 资源路径
     * @param destFilePath   目标路径
     */
    public static void moveFile(String sourceFilePath, String destFilePath) {
        if (StrUtil.isEmpty(sourceFilePath) || StrUtil.isEmpty(destFilePath)) {
            throw new RuntimeException("Both sourceFilePath and destFilePath cannot be null.");
        }
        moveFile(new File(sourceFilePath), new File(destFilePath));
    }

    /**
     * move file
     *
     * @param srcFile
     * @param destFile
     */
    public static void moveFile(File srcFile, File destFile) {
        boolean rename = srcFile.renameTo(destFile);
        if (!rename) {
            copyFile(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
            deleteFile(srcFile.getAbsolutePath());
        }
    }

    /**
     * 复制文件
     *
     * @param sourceFilePath
     * @param destFilePath
     * @return
     * @throws RuntimeException if an error occurs while operator FileOutputStream
     */
    public static boolean copyFile(String sourceFilePath, String destFilePath) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(sourceFilePath);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("FileNotFoundException occurred. ", e);
        }
        return writeFile(destFilePath, inputStream);
    }

    /**
     * 读取文件转成List 每一行为一个集合元素
     */
    public static List<String> readFileToList(String filePath, String charsetName) {
        File file = new File(filePath);
        List<String> fileContent = new ArrayList<String>();
        if (file == null || !file.isFile()) {
            return null;
        }

        BufferedReader reader = null;
        try {
            InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
            reader = new BufferedReader(is);
            String line = null;
            while ((line = reader.readLine()) != null) {
                fileContent.add(line);
            }
            reader.close();
            return fileContent;
        } catch (IOException e) {
            throw new RuntimeException("IOException occurred. ", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException("IOException occurred. ", e);
                }
            }
        }
    }

    /**
     * 根据路径得到文件名 可以相对路径与绝对路径 返回值：不包括扩展名
     */
    public static String getFileNameWithoutExtension(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            return filePath;
        }

        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
        int filePosi = filePath.lastIndexOf(File.separator);
        if (filePosi == -1) {
            return (extenPosi == -1 ? filePath : filePath.substring(0, extenPosi));
        }
        if (extenPosi == -1) {
            return filePath.substring(filePosi + 1);
        }
        return (filePosi < extenPosi ? filePath.substring(filePosi + 1, extenPosi) : filePath.substring(filePosi + 1));
    }

    /**
     * 根据路径得到文件名 可以相对路径与绝对路径 返回值：包括扩展名
     */
    public static String getFileName(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            return filePath;
        }

        int filePosi = filePath.lastIndexOf(File.separator);
        return (filePosi == -1) ? filePath : filePath.substring(filePosi + 1);
    }

    /**
     * 根据文件路径得到所在的目录名
     */
    public static String getFolderName(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            return filePath;
        }
        filePath = filePath.replace(File.separator, "/");
        int filePosi = filePath.lastIndexOf("/");
        return (filePosi == -1) ? "" : filePath.substring(0, filePosi);
    }

    /**
     * 从文件路径中获得文件的拓展名
     */
    public static String getFileExtension(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            return filePath;
        }
        int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
        int filePosi = filePath.lastIndexOf(File.separator);
        if (extenPosi == -1) {
            return "";
        }
        return (filePosi >= extenPosi) ? "" : filePath.substring(extenPosi + 1);
    }

    /**
     * 新建文件目录
     */
    public static boolean makeDirs(String filePath) {
        String folderName = getFolderName(filePath);
        if (StrUtil.isEmpty(folderName)) {
            return false;
        }

        File folder = new File(folderName);
        return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
    }

    /**
     * 新建文件目录
     */
    public static boolean makeFolders(String filePath) {
        return makeDirs(filePath);
    }

    /**
     * 判断文件是否存在
     */
    public static boolean isFileExist(String filePath) {
        if (StrUtil.isEmpty(filePath)) {
            return false;
        }

        File file = new File(filePath);
        return (file.exists() && file.isFile());
    }

    /**
     * 判断目录是否存在
     */
    public static boolean isFolderExist(String directoryPath) {
        if (StrUtil.isEmpty(directoryPath)) {
            return false;
        }

        File dire = new File(directoryPath);
        return (dire.exists() && dire.isDirectory());
    }

    /**
     * 删除文件
     */
    public static boolean deleteFile(String path) {
        if (StrUtil.isEmpty(path)) {
            return true;
        }

        File file = new File(path);
        if (!file.exists()) {
            return true;
        }
        if (file.isFile()) {
            return file.delete();
        }
        if (!file.isDirectory()) {
            return false;
        }
        for (File f : file.listFiles()) {
            if (f.isFile()) {
                f.delete();
            } else if (f.isDirectory()) {
                deleteFile(f.getAbsolutePath());
            }
        }
        return file.delete();
    }

    /**
     * 得到文件的大小 如果文件不存在或着不是文件返回-1
     */
    public static long getFileSize(String path) {
        if (StrUtil.isEmpty(path)) {
            return -1;
        }

        File file = new File(path);
        return (file.exists() && file.isFile() ? file.length() : -1);
    }

    /**
     * 根据路径得到文件byte
     *
     * @param filePath
     * @return
     */
    public static byte[] getFileByte(String filePath) {
        byte[] buffer = null;
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    /**
     * 根据文件得到byte
     *
     * @param file
     * @return
     */
    public static byte[] getFileByte(File file) {
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    // 根据byte保存文件
    public static void saveFileFromByte(byte[] fileByte, String filePath) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
                dir.mkdirs();
            }
            file = new File(filePath);
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(fileByte);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }


    /**
     * 合并父子路径(处理路径分隔符)
     *
     * @param parent 父路径
     * @param child  子路径
     * @return 结果
     */
    public static String combinePath(String parent, String child) {
        if (StrUtil.isEmpty(parent)) return child;
        if (StrUtil.isEmpty(child)) return parent;
        int pn = parent.length();
        int cn = child.length();
        String c = child;
        int childStart = 0;
        int parentEnd = pn;

        if ((cn > 1) && (c.charAt(0) == SEPARATOR_CHAR)) {
            if (c.charAt(1) == SEPARATOR_CHAR) {
                childStart = 2;
            } else {
                childStart = 1;

            }
            if (cn == childStart) {
                if (parent.charAt(pn - 1) == SEPARATOR_CHAR)
                    return parent.substring(0, pn - 1);
                return parent;
            }
        }

        if (parent.charAt(pn - 1) == SEPARATOR_CHAR)
            parentEnd--;

        int strlen = parentEnd + cn - childStart;
        char[] theChars = null;
        if (child.charAt(childStart) == SEPARATOR_CHAR) {
            theChars = new char[strlen];
            parent.getChars(0, parentEnd, theChars, 0);
            child.getChars(childStart, cn, theChars, parentEnd);
        } else {
            theChars = new char[strlen + 1];
            parent.getChars(0, parentEnd, theChars, 0);
            theChars[parentEnd] = SEPARATOR_CHAR;
            child.getChars(childStart, cn, theChars, parentEnd + 1);
        }
        return new String(theChars);
    }
}